[2018_AceBear Security Contest] [REV] secure login

문제내용

Please run solution using ubuntu 16.04 Time of server is UTC+000 Service: nc securelogin.acebear.site 5001

문제 풀이

먼저 바이너리 파일을 리눅스에서 실행해보면 다음과 같다.

$ ./secure_login
**************************Welcome to secure login**************************
*                                                                         *
*************************Challenge Created By CNV**************************
*   Team: AceBear                                                         *
*   My blog: https://chung96vn.blogspot.com/                              *
***************************************************************************
Current time: Tue Jan 30 20:42:24 2018

Give me your name:
Welcome:
Gime me your password:
Generated password:
$

name과 password를 입력값으로 하여 일치여부를 확인하는 문제이다.


IDA hexray decompiler

제공해준 파일의 코드 중 패스워드를 암호화 하는 부분의 코드는 다음과 같았다. 패스워드 암호화 시 rand함수를 사용한다.

void *__cdecl sub_8048950(int a1)
{
    __int16 v1; // ST24_2@5
    __int16 v2; // ST28_2@5
    __int16 v3; // ax@5
    signed int i; // [sp+0h] [bp-28h]@4
    int v6; // [sp+4h] [bp-24h]@4
    void *s; // [sp+8h] [bp-20h]@1
    char *nptr; // [sp+Ch] [bp-1Ch]@4
    char *v9; // [sp+10h] [bp-18h]@4

    s = malloc(0x40u);
    if ( !s )
    {
        puts("Can not malloc!");
        exit(0);
    }
    memset(s, 0, 0x40u);
    nptr = (char *)malloc(0x10u);
    v9 = (char *)malloc(0x10u);
    LOWORD(v6) = 0;
    for ( i = 0; i <= 15; ++i )
    {
        *(_DWORD *)nptr = *(_DWORD *)(4 * i + a1);
        *(_DWORD *)v9 = dword_804B0C0[i];
        v1 = strtoul(nptr, (char **)nptr + 2, 16);
        v2 = strtoul(v9, (char **)v9 + 2, 16);
        v3 = rand();
        v6 = (unsigned __int16)(v2 * ((v6 ^ v1 ^ v3) + 1) + (v6 ^ v1 ^ v3));
        sprintf((char *)s + 4 * i, "%04X", v6);
    }
    return s;
}

rand함수가 있으므로 seed가 존재하는 지 찾아보니 실행 시 Current time을 뿌려주면서 seed도 결정하는 것으로 보인다.

seed = time(0);
srand(seed);

radare2를 통한 디버깅

[0xf77d6a20]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
= attach 11008 11008
11008
[0xf77d6a20]> dcu main
Continue until 0x08048bc4 using 1 bpsize
hit breakpoint at: 8048bc4

기본 명령어

V + p + p (disassembly)
step into (s)
step over (S)
:px 200@[address]

for 문이 동작하는 부분을 집중적으로 디버깅하며 입력값의 변화를 확인하자. 패스워드는 입력값 "1234567890123456789012345678901234567890123456789012345678901234"을 입력하였다.

.text:08048A74 loc_8048A74:
.text:08048A74    cmp     [ebp+var_28], 0Fh
.text:08048A78    jle     loc_80489CB
.text:080489CB loc_80489CB:
.text:080489CB    mov     eax, [ebp+var_28]     // i=0~15
.text:080489CE    shl     eax, 2                // 4*i
.text:080489D1    mov     edx, eax
.text:080489D3    mov     eax, [ebp+arg_0]
.text:080489D6    add     eax, edx              // a1[4*i]
.text:080489D8    mov     edx, [eax]
.text:080489DA    mov     eax, [ebp+nptr]
.text:080489DD    mov     [eax], edx            // nptr=eax
.text:080489DF    mov     eax, [ebp+var_28]     // i=0~15
.text:080489E2    shl     eax, 2                // 4*i
.text:080489E5    add     eax, 804B0C0h
.text:080489EA    mov     edx, [eax]
.text:080489EC    mov     eax, [ebp+var_18]
.text:080489EF    mov     [eax], edx
.text:080489F1    mov     eax, [ebp+nptr]
.text:080489F4    add     eax, 8
.text:080489F7    sub     esp, 4
.text:080489FA    push    10h             ; base
.text:080489FC    push    eax             ; endptr
.text:080489FD    push    [ebp+nptr]      ; nptr
.text:08048A00    call    _strtoul
.text:08048A05    add     esp, 10h
.text:08048A08    mov     [ebp+var_14], eax     // v1
.text:08048A0B    mov     eax, [ebp+var_18]
.text:08048A0E    add     eax, 8
.text:08048A11    sub     esp, 4
.text:08048A14    push    10h             ; base
.text:08048A16    push    eax             ; endptr
.text:08048A17    push    [ebp+var_18]    ; nptr
.text:08048A1A    call    _strtoul
.text:08048A1F    add     esp, 10h
.text:08048A22    mov     [ebp+var_10], eax     // v2
.text:08048A25    call    _rand
.text:08048A2A    movzx   eax, ax               // v3=eax
.text:08048A2D    xor     eax, [ebp+var_14]     // v3^v1
.text:08048A30    xor     eax, [ebp+var_24]     // v3^v1^v6
.text:08048A33    mov     [ebp+var_C], eax
.text:08048A36    mov     eax, [ebp+var_C]
.text:08048A39    add     eax, 1                // v3^v1^v6 + 1
.text:08048A3C    imul    eax, [ebp+var_10]     // (v3^v1^v6 + 1) * v2
.text:08048A40    mov     edx, eax
.text:08048A42    mov     eax, [ebp+var_C]      // (v3^v1^v6)
.text:08048A45    add     eax, edx              // ((v3^v1^v6 + 1) * v2) + (v3^v1^v6)
.text:08048A47    and     eax, 0FFFFh           // (((v3^v1^v6 + 1) * v2) + (v3^v1^v6))&0xffff
.text:08048A4C    mov     [ebp+var_24], eax     // v6 = (((v3^v1^v6 + 1) * v2) + (v3^v1^v6))&0xffff
.text:08048A4F    mov     eax, [ebp+var_28]
.text:08048A52    shl     eax, 2
.text:08048A55    mov     edx, eax
.text:08048A57    mov     eax, [ebp+s]
.text:08048A5A    add     eax, edx
.text:08048A5C    sub     esp, 4
.text:08048A5F    push    [ebp+var_24]
.text:08048A62    push    offset format   ; "%04X"
.text:08048A67    push    eax             ; s
.text:08048A68    call    _sprintf              // s[4*i]
.text:08048A6D    add     esp, 10h
.text:08048A70    add     [ebp+var_28], 1

다음 코드를 통해 브루트포싱을 진행

from pwn import *
from ctypes import *

key = "1234567890123456789012345678901234567890123456789012345678901234"
correct = "F05664E983F54E5FA6D5D4FFC5BF930743F60D8FC2C78AFBB0AF7C82664F2043"

libc = CDLL("libc.so.6")

p=process("./secure_login")
#p=remote('securelogin.acebear.site', 5001)
seed = libc.time(0)
libc.srand(seed)
h = 0
pw = ''
for n in range(16):
    r = libc.rand()
    for l in range(0x10000):
        if (int(key[4*n:4*n+4],16)*((h^r^l)+1)+(h^r^l))&0xffff==int(correct[4*n:4*n+4],16):
            pw += "%04X" % l
            h = int(correct[4*n:4*n+4],16)
            break

print p.sendlineafter('name: ','joizel'),
print p.sendlineafter(': ', pw)
print p.recv(1024)

p.interactive()